<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * 上面：
 * 这个BASEPATH，就是在入口文件(index.php)里面定义的那个BASEPATH～
 * 如果没有定义BASEPATH，那么直接退出，下面程序都不执行。其实除了入口文件index.php开头没有这句话之外，所有文件都会有这句话
 * 也就是说，所有文件都不能单独运行，一定是index.php在运行过程中把这些文件通
 * 过某种方式引进来运行，所以只有入口文件index.php才能被访问。
 *
 */

/**
 * CodeIgniter
 *
 */

/**
 * 弱弱地建议：
 *  其实把CodeIgniter.php这个文件的代码运行一次，就是整个CI应用都完成了一次完整的运作流程了。
 *  其中会加载一些组件，引入很多外部文件，等等。所以建议在阅读此文件代码的时候，第一遍先阅读它的
 *  大概流程，也就是说不必进入相应的组件、函数文件中去。第二遍看的时候才具体看那些函数、组件里面是怎
 *  么实现的。当然要看个人需要咯～
 *
 */

// ------------------------------------------------------------------------

/**
 * System Initialization File
 */

/**
 * CodeIgniter Version
 *
 * @var string
 *
 */
define('CI_VERSION', '2.1.2');

/**
 * CodeIgniter Branch (Core = TRUE, Reactor = FALSE)
 *
 * @var boolean
 *
 */
define('CI_CORE', FALSE);

/*
 * ------------------------------------------------------
 *  Load the global functions
 * ------------------------------------------------------
 */
//在这里引入了很多全局函数。这些函数在整个应用运行的过程都是非常重要的。
//这些函数都是应该最先被引入，起码要先于下面的组件，为什么呢？详见core/Common.php，建议在这个地方先略过。
require(BASEPATH.'core/Common.php');

/*
 * ------------------------------------------------------
 *  Load the framework constants
 * ------------------------------------------------------
 */

//加载配置的常量。这个配置文件里面默认已经有一些和文件有关的常量。

//下面这个判断可以看出一开始我们在index.php里面定义的那个ENVIRONMENT的作用之一，如果是定义某个环境，
//会调用相应的配置文件，这样就可以使得应用在相应的环境中运行。不仅仅是这个常量的配置文件是这样子，
//以后你会发现，其实全部配置文件都是先判断当前环境再引入。
//方便切换，只需在index.php里面改一下ENVIRONMENT的值。
if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php'))
{
    require(APPPATH.'config/'.ENVIRONMENT.'/constants.php');
}
else
{
    //当然啦，如果压根没有这个环境下的配置文件，就会调用默认的。CI手册上也有说，各种环境下的相同的配置文件，可以直接放在
    //config/下，而不需要每个环境的目录下都有。
    require(APPPATH.'config/constants.php');
}

/*
 * ------------------------------------------------------
 *  Define a custom error handler so we can log PHP errors
 * ------------------------------------------------------
 */
//这里是把错误的处理交给_exception_handler这个函数来做，是在core/Common.php文件中定义的全局函数。
//具体请看core/Common.php。
set_error_handler('_exception_handler');

if ( ! is_php('5.3'))
{
    //这个magic_quotes就是会自动把那些由$_GET,$_POST等传过来的值进行处理，加\。
    //这个东西最好不要打开，虽然某些时候会帮到忙，不过过滤、转义等处理最后还是手动做好一些。
    //php5.3以上默认是把这个东西关掉的(linux下/etc/php.ini里面)。这个东西本来就不应该有。
    @set_magic_quotes_runtime(0); // Kill magic quotes
}

/*
 * ------------------------------------------------------
 *  Set the subclass_prefix
 * ------------------------------------------------------
 *
 */
/**
 * 这个subclass_prefix是什么？
 * 这是一个非常棒的东西！！有了它我们就可以轻易扩展CI框架～
 * 具体core/Common.php 中的load_class()
 */

if (isset($assign_to_config['subclass_prefix']) AND $assign_to_config['subclass_prefix'] != '')
{
    //这个get_config($replace)就是从配置文件里面读取信息，这里是读取config/config.php中的配置信息
    //这个参数$replace的作用是什么呢？就是临时把修改配置文件的意思，注意并没有从改变文件的值，这个改变只是
    //停留在内存的层面上。
    //而$assign_to_config['xxx'];是在index.php中定义的一个配置信息数组，这个配置数组要优先权要大于配置文件当中的。
    //所以这个判断的作用是，看看有没有在index.php里面定义了 $assign_to_config['subclass_prefix']，如果有的话，
    //就那把配置文件中的$config['subclass_prefix']的值改成这个。
    get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix']));
}

/*
 * ------------------------------------------------------
 *  Set a liberal script execution time limit
 *  上面这个'liberal'是“慷慨大方的”的意思 -_-||
 * ------------------------------------------------------
 */
//这个safe_mode也是php.ini里面的一个配置项目
//具体看：http://blog.163.com/wu_guoqing/blog/static/196537018201272710925363/
//这里主要是设置运行时间限制而已。
if (function_exists("set_time_limit") == TRUE AND @ini_get("safe_mode") == 0)
{
    @set_time_limit(300);
}

/*
 * ------------------------------------------------------
 *  Start the timer... tick tock tick tock...
 * ------------------------------------------------------
 */
//load_class()是用来加载类的，准确来说，是用来取得某个类的一个单一实例。
//像下来的调用就是返回system/core/Benchmark的一个实例。core里面的大都是CI的组件。

/**
 * Benchmark是基准点的意思。
 * 其实这个类的功能很简单，只是用来计算程序运行消耗的时间和内存而已。
 * 以后经常在某个地方冒出来句$BM->mark('xxx');这个作用就是记录运行到当前位置的时候的时间点。
 * 通过两个时间点，就可以计算出时间了。
 */
$BM =& load_class('Benchmark', 'core');
$BM->mark('total_execution_time_start');
$BM->mark('loading_time:_base_classes_start');

/*
 * ------------------------------------------------------
 *  Instantiate the hooks class
 * ------------------------------------------------------
 */
//取得Hooks组件。
/**
 * 这个hook也是非常非常棒的一个东西！它可以让我们很好地扩展和改造CI～
 * 可以这样理解，一个应用从运行到结束这个期间，CI为我们保留了一些位置，在这些位置上面可以让开发人员放上所谓的
 * “钩子”（其实就是一段程序啦！），在应用运行过程中，当运行到有可以放钩子的位置的时候，先检测开发人员有没有
 * 实现这里的钩子，如果有就运行它。
 * 有些地方甚至可以用自己写的钩子程序替代CI框架本来的程序。
 */
$EXT =& load_class('Hooks', 'core');

/*
 * ------------------------------------------------------
 *  Is there a "pre_system" hook?
 * ------------------------------------------------------
 */
//这里就有一个钩子啦。大概意思是：整个应用系统开始动了，这里要不要先让开发人员来一段程序？
//如果你定义了pre_system这个钩子，那么，其实它就是在这个位置运行的。
$EXT->_call_hook('pre_system');

/*
 * ------------------------------------------------------
 *  Instantiate the config class
 * ------------------------------------------------------
 */
//取得配置组件。
$CFG =& load_class('Config', 'core');

// Do we have any manually set config items in the index.php file?

//如果有在index.php定义配置数组，那么就丢给配置组件CFG，以后就由CFG来保管了配置信息了。
if (isset($assign_to_config))
{
    $CFG->_assign_to_config($assign_to_config);
}

/*
 * ------------------------------------------------------
 *  Instantiate the UTF-8 class
 * ------------------------------------------------------
 *
 *
 */

//取得UTF－8组件，这里暂时不用管它。
$UNI =& load_class('Utf8', 'core');

/*
 * ------------------------------------------------------
 *  Instantiate the URI class
 * ------------------------------------------------------
 */
//取得URI组件。
$URI =& load_class('URI', 'core');

/*
 * ------------------------------------------------------
 *  Instantiate the routing class and set the routing
 * ------------------------------------------------------
 */

//取得URI的好基友RTR
$RTR =& load_class('Router', 'core');

//RTR的这个_set_routing();其实做了非常多的事情。。详见core/Router.php。非常重要。
$RTR->_set_routing();

// Set any routing overrides that may exist in the main index file
//
if (isset($routing))
{
    //这个$routing是在index.php入口文件中可以配置的一个数组。这里起到路由覆盖的作用。
    //index.php里面配置的信息永远都是最优先的。
    //在这里无论你请求的路由是什么，只要有配置$routing（当然要配对），就会被它重定向。
    //所以我觉得这句话放在这个地方有点坑，上面_set_routing搞了那么久，一下子就被覆盖掉了。
    $RTR->_set_overrides($routing);
}

/*
 * ------------------------------------------------------
 *  Instantiate the output class
 * ------------------------------------------------------
 */
//输出组件。这个输出组件有什么用？输出不是$this->load->view()么？其实它们两个也是好基友。
//详见：core/Output.php core/Loader.php
$OUT =& load_class('Output', 'core');

/*
 * ------------------------------------------------------
 * Is there a valid cache file?  If so, we're done...
 * ------------------------------------------------------
 */
//下面是输出缓存的处理，这里允许我们自己写个hook来取替代CI原来Output类的缓存输出。
if ($EXT->_call_hook('cache_override') === FALSE)
{
    if ($OUT->_display_cache($CFG, $URI) == TRUE)
    {
        exit;//如果可以输出缓存，那么就没有必要再做其它事了。输出结果后直接退出。
    }
}

/*
 * -----------------------------------------------------
 * Load the security class for xss and csrf support
 * -----------------------------------------------------
 */
//取得安全组件（安全组件暂时不详讲，因为对于CI一个运作流程来说，它不是必要的。CI的安全处理以后会作为一个新话题来探讨）
$SEC =& load_class('Security', 'core');

/*
 * ------------------------------------------------------
 *  Load the Input class and sanitize globals
 * ------------------------------------------------------
 */
//取得安全组件的好基友INPUT组件。（主要是结合安全组件作一些输入方面的安全处理，$this->input->post()这些常用的操作都是
//由它们两个负责的。）
$IN =& load_class('Input', 'core');

/*
 * ------------------------------------------------------
 *  Load the Language class
 * ------------------------------------------------------
 */
//语言组件。
$LANG =& load_class('Lang', 'core');

/*
 * ------------------------------------------------------
 *  Load the app controller and local controller
 * ------------------------------------------------------
 *
 */
//引入控制器父类文件。这里和其它组件的引入方式不一样，用load_class();因为我们最终用到的并不是这个父类，
//而是我们自己写在application/controllers/下的某个由uri请求的控制器。
require BASEPATH.'core/Controller.php';

//定义get_instance();方法，通过调用CI_Controller::get_instance()可以实现单例化，
//调用此函数可方便以后直接取得当前应用控制器。
function &get_instance()
{
    return CI_Controller::get_instance();
}


//和其它组件一样，控制器父类同样可以通过前缀的方式进行扩展。
if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
{
    require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
}

if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php'))
{
    //其实如果能够进入这里，说明了上面的$RTR->_set_routing();在_validate_request()的时候一定是在请求默认控制器。
    //详见：core/Router.php
    show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.');
}

//引入我们最终确认的应用控制器。
include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php');

$BM->mark('loading_time:_base_classes_end');//这里mark一下，说明CI的所需要的基本的类都加载完了。

/*
 * ------------------------------------------------------
 *  Security check
 * ------------------------------------------------------
 *
 *  下面主要进行一些方法上的验证。
 *  因为毕竟我们是通过URI直接调用控制器里面的方法的，其实这是个很危险的事情。
 *  必须要保证我们原本没想过要通过URI访问的方法不能访问。
 *
 *  CI里面规定以_下划线开头的方法，一般是作为非公开的方法，即使方法定义为public。
 *  其实不仅仅是CI这么做，把非公开的方法名以_开头，是很好的一种规范。
 *  第二个就是父类CI_Controller里面的方法也是不允许通过URI访问的。
 *  如果URI请求这样的方法，那么会作为404处理。
 */
$class  = $RTR->fetch_class();
$method = $RTR->fetch_method();

if ( ! class_exists($class)
    OR strncmp($method, '_', 1) == 0
    OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller')))
)
{
    if ( ! empty($RTR->routes['404_override']))
    {
        $x = explode('/', $RTR->routes['404_override']);
        $class = $x[0];
        $method = (isset($x[1]) ? $x[1] : 'index');
        if ( ! class_exists($class))
        {
            if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
            {
                show_404("{$class}/{$method}");
            }

            include_once(APPPATH.'controllers/'.$class.'.php');
        }
    }
    else
    {
        show_404("{$class}/{$method}");
    }
}

/*
 * ------------------------------------------------------
 *  Is there a "pre_controller" hook?
 * ------------------------------------------------------
 */
//这里又一个钩子，这个钩子的位置往往都是在一些特殊的位置的，像这里就是发生在实例化控制器前。
$EXT->_call_hook('pre_controller');

/*
 * ------------------------------------------------------
 *  Instantiate the requested controller
 * ------------------------------------------------------
 */
// Mark a start point so we can benchmark the controller
$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start');

$CI = new $class();//折腾了这么久，终于实例化我们想要的控制器了。。。。。。。。。。。。。。。。。。。。。

/*
 * ------------------------------------------------------
 *  Is there a "post_controller_constructor" hook?
 * ------------------------------------------------------
 */
$EXT->_call_hook('post_controller_constructor');

/*
 * ------------------------------------------------------
 *  Call the requested method
 * ------------------------------------------------------
 */
// Is there a "remap" function? If so, we call it instead
if (method_exists($CI, '_remap'))
{
    //这个_remap也是一个非常棒的东西！如果有定义，它会优先被调用，在这个方法里面我们可以根据$method和参数，随意
    //做任何处理和加工。即使那个$method不存在。
    $CI->_remap($method, array_slice($URI->rsegments, 2));
}
else
{

    //如果请求的方法不存在，那么就按404处理。
    if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI))))
    {
        // Check and see if we are using a 404 override and use it.
        if ( ! empty($RTR->routes['404_override']))
        {
            $x = explode('/', $RTR->routes['404_override']);
            $class = $x[0];
            $method = (isset($x[1]) ? $x[1] : 'index');
            if ( ! class_exists($class))
            {
                if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
                {
                    show_404("{$class}/{$method}");
                }

                include_once(APPPATH.'controllers/'.$class.'.php');
                unset($CI);//这里是把原来的控制器删掉，改用我们定义的404重定向的类。
                $CI = new $class();
            }
        }
        else
        {
            show_404("{$class}/{$method}");
        }
    }


    //终于调用了！！！！！！！！！！！！！！！！！！！！！！！！！！！就在这里。
    //不过，不是打击你，虽然我们请求的控制器的那个方法被调用了，但是实际上，我们想要的输出并没有完全输出来。
    //这就是因为$this->load->view();并不是马上输出结果，而是把结果放到缓冲区，然后最后Output类把它冲出来。
    call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));
}


// Mark a benchmark end point
$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');

/*
 * ------------------------------------------------------
 *  Is there a "post_controller" hook?
 * ------------------------------------------------------
 */
$EXT->_call_hook('post_controller');

/*
 * ------------------------------------------------------
 *  Send the final rendered output to the browser
 * ------------------------------------------------------
 */
if ($EXT->_call_hook('display_override') === FALSE)
{
    $OUT->_display();//这里，把$this->load->view();里面缓冲的输出结果输出，基本上一个流程总算完成了。详见core/Output.php。
}

/*
 * ------------------------------------------------------
 *  Is there a "post_system" hook?
 * ------------------------------------------------------
 */
$EXT->_call_hook('post_system');

/*
 * ------------------------------------------------------
 *  Close the DB connection if one exists
 * ------------------------------------------------------
 */
if (class_exists('CI_DB') AND isset($CI->db))
{
    $CI->db->close();//关闭连接。
}